package com.katze.boot.plugins.redis;

import com.katze.boot.plugins.redis.operations.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.data.redis.connection.RedisConnectionCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.net.ConnectException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 说  明：Redis配置信息
 * 作  者：zf.zeng
 * 日  期：2021-02-14 (星期日)
 **/
@Component
@Import(RedisRegister.class)
@ConditionalOnProperty(prefix = "spring.redis", name = {"host", "port"})
public class RedisAutoConfiguration implements ApplicationContextAware {
    private static final Logger log = LoggerFactory.getLogger(RedisAutoConfiguration.class);

    private ApplicationContext applicationContext;

    @Bean
    public <T>RedisTemplateManager<T> redisTemplateManager() {
        String database = "0";
        Map<Integer, RedisTemplate<String, T>> redisTemplateMap = new ConcurrentHashMap<>();
        // 根据多个库实例化出多个连接池和Template
        for (Map.Entry<String, Boolean> item : RedisRegister.REDIS_TEMPLATE_POOL.entrySet()) {
            String name = RedisTemplate.class.getSimpleName() + item.getKey();
            RedisTemplate<String, T> redisTemplate = applicationContext.getBean(name, RedisTemplate.class);
            redisTemplateMap.put(Integer.parseInt(item.getKey()), redisTemplate);
            if (item.getValue()) {
                database = item.getKey();
            }
            log.info("Init RedisTemplate Database {} Success  !!!!", item.getKey());
        }
        if (redisTemplateMap.size() == 0) {
            throw new RuntimeException("load redisTemplate failure , please  check spring.redis property config！！！");
        }

        return new RedisTemplateManager<>(redisTemplateMap, Integer.parseInt(database));
    }

    @Bean
    @ConditionalOnMissingBean(RedisOperations.class)
    public RedisOperations<?> redisOperations(RedisTemplateManager<?> redisTemplateManager){
        return new RedisOperations<>(redisTemplateManager);
    }

    /**
     * 对key-value类型数据操作
     */
    @Bean
    @ConditionalOnMissingBean(RedisValueOperations.class)
    public RedisValueOperations<?> redisValueOperations(RedisTemplateManager<?> redisTemplateManager) {
        return new RedisValueOperations<>(redisTemplateManager);
    }

    /**
     * 对hash类型的数据操作
     */
    @Bean
    @ConditionalOnMissingBean(RedisHashOperations.class)
    public RedisHashOperations<?> redisHashOperations(RedisTemplateManager<?> redisTemplateManager) {
        return new RedisHashOperations<>(redisTemplateManager);
    }

    /**
     * 对链表类型的数据操作
     */
    @Bean
    @ConditionalOnMissingBean(RedisListOperations.class)
    public RedisListOperations<?> redisListOperations(RedisTemplateManager<?> redisTemplateManager) {
        return new RedisListOperations<>(redisTemplateManager);
    }

    /**
     * 对无序集合类型的数据操作
     */
    @Bean
    @ConditionalOnMissingBean(RedisSetOperations.class)
    public RedisSetOperations<?> redisSetOperations(RedisTemplateManager<?> redisTemplateManager) {
        return new RedisSetOperations<>(redisTemplateManager);
    }

    /**
     * 对有序集合类型的数据操作
     */
    @Bean
    @ConditionalOnMissingBean(RedisZSetOperations.class)
    public RedisZSetOperations<?> zSetOperations(RedisTemplateManager<?> redisTemplateManager) {
        return new RedisZSetOperations<>(redisTemplateManager);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public boolean ping() throws ConnectException {
        RedisOperations<String> operations = this.applicationContext.getBean(RedisOperations.class);
        for (String index : RedisRegister.REDIS_TEMPLATE_POOL.keySet()) {
            String str = operations.select(Integer.parseInt(index), p -> p.execute(RedisConnectionCommands::ping));
            if (str.equals("PONG")) {
                log.info("Redis DB{} 连接成功!", index);
            } else {
                throw new ConnectException("Redis DB" + index + " 连接失败");
            }
        }
        return true;
    }
}
